#include <header.h>
#include <iostream>
#include <asm-generic/socket.h>

#define MAX_EVENT_SIZE 100
#define EPOLL_TIMEOUT 5000

int main()
{
    //1. 创建监听的套接字
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0) {
        perror("socket");
        return EXIT_FAILURE;
    }
    printf(">> listenfd: %d\n", listenfd);

    //设置套接字属性:网络地址可以重用
    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8000);
    //serveraddr.sin_addr.s_addr = inet_addr("192.168.30.129");
    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//代表本机地址

    //2. 绑定网络地址
    int ret = bind(listenfd, (const struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret < 0) {
        perror("bind");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //3. 监听新连接的到来
    ret = listen(listenfd, 1);
    if(ret < 0) {
        perror("listen");
        close(listenfd);
        return EXIT_FAILURE;
    }

    //4. 创建epoll的实例
    int epfd = epoll_create1(0);
    if(epfd < 0) {
        perror("epoll_create1");
        close(listenfd);
        return EXIT_FAILURE;
    }
    printf(">> epfd: %d\n", epfd);

    //5.epoll添加对listenfd事件的监听
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.fd = listenfd;
    ev.events = EPOLLIN;//对listenfd上的读事件进行监听
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
    if(ret < 0) {
        perror("epoll_ctl");
        close(listenfd);
        close(epfd);
        return EXIT_FAILURE;
    }

    struct epoll_event evtArr[MAX_EVENT_SIZE] = {0};
    //6.事件循环，不断的对事件进行监听
    while(1) {
        int nready = epoll_wait(epfd, evtArr, MAX_EVENT_SIZE, EPOLL_TIMEOUT);
        if(nready == -1 && errno == EINTR) {
            continue;//被信号影响
        } else if(nready == -1) {
            perror("epoll_wait");
        } else if(0 == nready) {
            printf("epoll timeout.\n");
        } else {
            printf(">> nready: %d\n", nready);
            //nready大于0的情况，有文件描述符就绪
            for(int i = 0; i < nready; ++i) {
                int fd = evtArr[i].data.fd;//遍历数组获取就绪的文件描述符
                
                if(fd == listenfd) {//listenfd的值为3
                    //6.1 有新连接过来
                    struct sockaddr_in clientaddr;
                    socklen_t len = sizeof(clientaddr);
                    int netfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
                    if(netfd < 0) {
                        perror("accept");
                    }

                    //6.2 新连接信息的打印
                    printf("conn %d tcp %s:%d-->%s:%d has connected.\n",
                           netfd, 
                           inet_ntoa(serveraddr.sin_addr),
                           ntohs(serveraddr.sin_port),
                           inet_ntoa(clientaddr.sin_addr),
                           ntohs(clientaddr.sin_port));
                    //6.3 epoll对netfd进行读事件的监听
                    ev.data.fd = netfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, netfd, &ev);
                } else {
                    //netfd已经建立好的连接上有读事件就绪
                    char buff[128] = {0};
                    //6.4 接收数据
                    ret = recv(fd, buff, sizeof(buff), 0);
                    if(0 == ret) {
                        //6.6 表示连接已经断开了
                        ev.data.fd = fd;//只需要指定文件描述符,不需要关心事件
                                        //因为是做删除操作
                        //从epoll监听的红黑树上删除
                        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
                        close(fd);
                        printf("conn %d has closed.\n", fd);
                        continue;
                    }
                    //6.5 连接正常情况下
                    printf("recv %d bytes, content: %s\n", ret, buff);
                    //服务器回显客户端的信息
                    ret = send(fd, buff, strlen(buff), 0);
                    printf("send %d bytes.\n", ret);
                    
                }
            }
        }
    }

    close(epfd);
    close(listenfd);
    return 0;
}


